閱讀順序:泛型函式 -> 泛型約束 -> 本篇 Ꮚ・ꈊ・Ꮚ
今天這篇內容比較輕鬆一點,原本想多寫泛型的進階功能 - 變異註釋,但看完後發現自己理解的不是很透徹,怕誤導大家,所以就先放棄了😊
我們可以為泛型中的型別參數設定「預設值」。當使用泛型時,沒有提供特定的型別參數,就會自動使用預設值
泛型參數預設值只需要在型別參數後使用等號(=)
給定預設型別即可
interface Container<T, U> {
element: T;
children: U;
}
// 無參數調用的情況
declare function create(): Container<HTMLDivElement, HTMLDivElement[]>;
// 指定型別參數 T
declare function create<T extends HTMLElement>(element: T): Container<T, T[]>;
// 指定型別參數 T, U
declare function create<T extends HTMLElement, U extends HTMLElement>(
element: T,
children: U[]
): Container<T, U[]>;
透過為泛型參數提供預設值,我們可以將上面多個 function overloads 簡化為一個更通用的寫法:
declare function create<T extends HTMLElement = HTMLDivElement, U extends HTMLElement[] = T[]>(
element?: T,
children?: U
): Container<T, U>;
如果沒有提供任何參數,則T 預設為 HTMLDivElement
、U 預設為 HTMLDivElement[]
使用泛型參數預設值的方式大大提高了函式的靈活性和可用性,減少了overloads 的數量
型別參數的可選性
如果一個型別參數有預設值,同時也是代表這個參數是可選的
型別參數的順序
require 的型別參數的位置不能在 optional 的型別參數之後
型別參數的約束滿足
如果有為型別參數定義約束條件的話,那預設值也需要符合這些條件
型別推斷(Type Inference)與預設值
如果指定了預設型別且型別推斷無法確定候選型別時,將被自動推斷為為預設型別
當型別參數可以透過上下文推斷得出時,自動推斷出的型別會優先於型別參數的預設值
Class 或 interface 擴展、合併
當 Class 或 interface 在現有結構上進行擴展、合併時,可以為現有的型別參數設定預設值,新的型別參數也是可以給
泛型也很常用在型別別名(type alias)上,如以下範例:
想了解型別別名的的話,可參考D21 - Type Alias 型別別名
// 定義一個型別別名泛型,包含 Data 和 Error
type ApiResponse<Data, Error> = {
success: boolean;
data: Data;
error: Error;
};
// 使用型別別名和泛型創建具體的型別
let response1: ApiResponse<string, string> = {
success: true,
data: "Data loaded successfully",
error: ""
};
let response2: ApiResponse<{ items: number }, Error> = {
success: false,
data: { items: 0 },
error: new Error("Failed to load data")
};
在這個例子中ApiResponse<Data, Error>
使得 ApiResponse 可以用來表示包含任何型別的資料和錯誤的處理
當使用型別別名泛型時,TypeScript 不會自動去做型別推斷。所以需要在使用時,明確指定型別參數,不然也可以像上方說的指定預設值
嘗試將 User
和 Product
重構,合併成一個自訂的 type,並使用泛型來進行優化
type User = {
id: string;
data: {
name: string;
age: number;
};
}
type Product = {
id: string;
data: {
name: string;
price: number;
};
}
function createUser(id: string, data: { name: string; age: number; }): User {
return { id, data };
}
function createProduct(id: string, data: { name: string; price: number; }): Product {
return { id, data };
}
const userEntry = createUser("user1", { name: "John Doe", age: 30 });
const productEntry = createProduct("product1", { name: "Apple iPhone 13", price: 799 });
每天的內容有推到 github 上喔